package jxtend.processor.eclipse.visitor.handler.impl;

import jxtend.processor.eclipse.utils.ASTNodeKind;
import jxtend.processor.eclipse.utils.ASTNodeUtils;
import jxtend.processor.eclipse.utils.EclipseProcessorUtils;
import jxtend.processor.eclipse.visitor.handler.ASTNodeHandlerVisitorContext;
import jxtend.processor.eclipse.visitor.handler.AbstractASTNodeVisitorHandler;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.lookup.Scope;

@SuppressWarnings("restriction")
public class ChainedCastingHandler extends AbstractASTNodeVisitorHandler {

	private static boolean isCastAlternative(ASTNodeHandlerVisitorContext context, ASTNode node) {
		return (node instanceof MessageSend)
			&& CharOperation.equals(JxtendTypeConstants.AS_METHOD, ((MessageSend) node).selector)
			&& ((MessageSend) node).arguments != null && ((MessageSend) node).arguments.length == 1
			&& (((MessageSend) node).arguments[0] instanceof NameReference);
	}

	@Override
	public boolean endVisitBeforeResolve(ASTNodeHandlerVisitorContext context, ASTNode node, Scope scope) {
		ASTNode parentNode = context.getParentNode();
		if (parentNode != null && isCastAlternative(context, parentNode)
				&& node == ((MessageSend) parentNode).arguments[0]) {
			return false;
		}

		boolean castAlternative = isCastAlternative(context, node);

		if (!castAlternative) {
			return true;
		}

		Expression argument = ((MessageSend) node).arguments[0];
		ASTNodeKind argumentNodeKind = ASTNodeKind.getEnum(argument);

		TypeReference castType;
		if (argument instanceof SingleNameReference) {
			long pos = EclipseProcessorUtils.encodePos(argument.sourceStart, argument.sourceEnd);
			castType =
				ASTNodeUtils.createSingleTypeReference(argumentNodeKind, ((SingleNameReference) argument).token, pos);
		} else {
			assert argument instanceof QualifiedNameReference;

			castType = ASTNodeUtils.createQualifiedTypeReference(argumentNodeKind,
					((QualifiedNameReference) argument).tokens, ((QualifiedNameReference) argument).sourcePositions);
		}

		CastExpression castExpression = new CastExpression(((MessageSend) node).receiver, castType);

		context.replaceNode(castExpression);
		return true;
	}
}
